package semantica;

import java.util.ArrayList;
import java.util.Hashtable;
import java.util.Iterator;
import semantica.tipos.ContenidoProc;
import semantica.tipos.ContenidoTupla;
import semantica.tipos.Tipo;
import semantica.tipos.TipoArray;
import semantica.tipos.TipoBoolean;
import semantica.tipos.TipoCharacter;
import semantica.tipos.TipoFloat;
import semantica.tipos.TipoInteger;
import semantica.tipos.TipoNatural;
import semantica.tipos.TipoError;
import semantica.tipos.TipoIdentificador;
import semantica.tipos.TipoProc;
import semantica.tipos.TipoTupla;

public class Semantica {

	/**

	 * 0- idDuplicado, 1-idNoDeclarado, 2-noEsConstante 3-noEsNatural 4-asignConstante 5-noTipoBasicoIn
         * 6- accesoTupla 7-callNumeroParametrosErroneo
	 * @param e1
	 * @param e2
	 * @return
	 */
	public static ArrayList<Boolean> unirErrores (ArrayList<Boolean> e1, ArrayList<Boolean> e2){
		ArrayList<Boolean> errores = new ArrayList<Boolean>();
		for (int i = 0; i < e1.size(); i++)
			errores.add(e1.get(i) | e2.get(i));
		return errores;
	} 
	
	/**
	 * Une 4 array list con errores haciendo una or logica 
	 * @param e1
	 * @param e2
	 * @param e3
	 * @param e4
	 * @return
	 */
	public static ArrayList<Boolean> unirErrores (ArrayList<Boolean> e1, ArrayList<Boolean> e2,
			ArrayList<Boolean> e3, ArrayList<Boolean> e4){
		return unirErrores(unirErrores(e1,e2),unirErrores(e3,e4));
	}
        /**
	 * Une 5 array list con errores haciendo una or logica 
	 * @param e1
	 * @param e2
	 * @param e3
	 * @param e4
         * @param e5
	 * @return
	 */
	
        public static ArrayList<Boolean> unirErrores (ArrayList<Boolean> e1, ArrayList<Boolean> e2,
			ArrayList<Boolean> e3, ArrayList<Boolean> e4, ArrayList<Boolean> e5){
		return unirErrores(unirErrores(e1,e2,e3),unirErrores(e4,e5));
	}
        
        public static ArrayList<Boolean> unirErrores (ArrayList<Boolean> e1, ArrayList<Boolean> e2,
			ArrayList<Boolean> e3){
		return unirErrores(unirErrores(e1,e2),e3);
	}
        
        /**
         * Añade al array list de booleanos, la or de b con lo que habia dentro del array en la pos dada
         * @param err
         * @param b
         * @param pos
         * @return 
         */
        public static ArrayList<Boolean> unirErrores (ArrayList<Boolean> err, boolean b, int pos){
            ArrayList<Boolean> error = new ArrayList<Boolean>(err);
            error.set(pos, error.get(pos) | b);
            return error;
        }
	
	/**
	 * Devuelve un array list con la copia de otro que le entra por parametro
	 * @param e1
	 * @return
	 */
	public static ArrayList<Boolean> copiaErrores (ArrayList<Boolean> e1){
            return new ArrayList<Boolean>(e1);
	}
	
	/**
	 * Crea un atributo tipo error. Es una array list con todos los errores a false.
	 * @return
	 */
	public static ArrayList<Boolean> creaErrores (){
		ArrayList<Boolean> errores = new ArrayList<Boolean>();
		for (int i = 0; i < 8; i++) /**FIXME CAMBIAR POR EL NUMERO DE ERRORES QUE HAYA TOTAL*/
			errores.add(false);
		return errores; 
	}

	/**
	 * Indica si hay algun tipo de error en el atributo de entrada.
	 * @param e
	 * @return
	 */
	public static boolean hayError(ArrayList<Boolean> e){
		boolean hayErrores = false;
		for (int i = 0; i < e.size(); i++)
			hayErrores |= e.get(i);
		return hayErrores;
	}
	
	/**
	 * Une 2 tipos para ver si alguno es error tipo
	 * @param t1
	 * @param t2
	 * @return
	 */
	public static Tipo unirTipos (Tipo t1, Tipo t2){
		
            if (t1.getTipo().equals("errorTipo"))
                return t1;
            if (t2.getTipo().equals("errorTipo"))
                return t2;
            return t1; // da igual cual devolvamos pues no es errorTipo
	}
	
        public static Tipo unirTipos (Tipo t1, Tipo t2, Tipo t3){
		
            if (t1.getTipo().equals("errorTipo"))
                return t1;
            if (t2.getTipo().equals("errorTipo"))
                return t2;
            if (t3.getTipo().equals("errorTipo"))
                return t3;
            return t1; // da igual cual devolvamos pues no es errorTipo
	}
        
	/**
	 * Une 4 tipos para ver si alguno es error tipo
	 * @param t1
	 * @param t2
	 * @param t3
	 * @param t4
	 * @return
	 */
	public static Tipo unirTipos (Tipo t1, Tipo t2, Tipo t3, Tipo t4){
            if (t1.getTipo().equals("errorTipo"))
                return t1;
            if (t2.getTipo().equals("errorTipo"))
                return t2;
            if (t3.getTipo().equals("errorTipo"))
                return t3;
            if (t4.getTipo().equals("errorTipo"))
                return t4;
            return t1; // da igual cual devolvamos pues no es errorTipo
	}
        public static Tipo unirTipos (Tipo t1, Tipo t2, Tipo t3, Tipo t4,Tipo t5){
            if (t1.getTipo().equals("errorTipo"))
                return t1;
            if (t2.getTipo().equals("errorTipo"))
                return t2;
            if (t3.getTipo().equals("errorTipo"))
                return t3;
            if (t4.getTipo().equals("errorTipo"))
                return t4;
            if (t5.getTipo().equals("errorTipo"))
                return t5;
            return t1; // da igual cual devolvamos pues no es errorTipo
	}
        /**
	 * Concatena dos arraylist con un string
	 * @param cod1
	 * @param cod2
	 * @param str
	 * @return
	 */	
	public static ArrayList<String> dameCod(ArrayList<String> cod1, ArrayList<String> cod2, String str,
                        ArrayList<Boolean> err, Tipo tipo){
		ArrayList<String> cod = new ArrayList<String>();
		cod.addAll(cod1);
                cod.addAll(cod2);
                cod.add(str);
		return cod;
	}
        
	
        
        public static ArrayList<String> copiaCod(ArrayList<String> cod, ArrayList<Boolean> err, Tipo tipo){
		if (!hayError(err) && !tipo.getTipo().equals("errorTipo")){
                       return new ArrayList<String>(cod);
                }
		return new ArrayList<String>();
	}
        
        public static ArrayList<String> copiaCod(ArrayList<String> cod){
            return new ArrayList<String>(cod);
	}
        
        
	/**
	 * Concatena dos arraylist con un string
	 * @param cod1
	 * @param cod2
	 * @param str
	 * @return
	 */	
	public static ArrayList<String> concatenaCod(ArrayList<String> cod1, ArrayList<String> cod2, String str,
                        ArrayList<Boolean> err, Tipo tipo){
		ArrayList<String> cod = new ArrayList<String>();
		if (!hayError(err) && !tipo.getTipo().equals("errorTipo")){
                    cod.addAll(cod1);
                    cod.addAll(cod2);
                    cod.add(str);
                }
		return cod;
	}
        
        /**
	 * Concatena dos arraylist con un string
	 * @param cod1
	 * @param cod2
	 * @param str
	 * @return
	 */	
	public static ArrayList<String> concatenaCod(ArrayList<String> cod1, ArrayList<String> cod2,
                        ArrayList<Boolean> err, Tipo tipo){
		ArrayList<String> cod = new ArrayList<String>();
		if (!hayError(err) && !tipo.getTipo().equals("errorTipo")){
                    cod.addAll(cod1);
                    cod.addAll(cod2);
                }
		return cod;
	}
        
        /**
	 * Concatena dos arraylist con un string
	 * @param cod1
	 * @param str
	 * @return
	 */	
	public static ArrayList<String> concatenaCod(ArrayList<String> cod1, String str, ArrayList<Boolean> err, Tipo tipo){
		ArrayList<String> cod = new ArrayList<String>();
		if (!hayError(err) && !tipo.getTipo().equals("errorTipo")){
                       cod.addAll(cod1);
                       cod.add(str);
                }
		return cod;
	}
        
        public static ArrayList<String> concatenaCod(ArrayList<String> cod1, ArrayList<Boolean> err, Tipo tipo){
		ArrayList<String> cod = new ArrayList<String>();
		if (!hayError(err) && !tipo.getTipo().equals("errorTipo")){
                       cod.addAll(cod1);
                }
		return cod;
	}
        
	/**
	 * Concatena 4 array list de strings
	 * @param cod1
	 * @param cod2
	 * @param cod3
	 * @return
	 */
	public static ArrayList<String> concatenaCod(ArrayList<String> cod1, ArrayList<String> cod2, 
			ArrayList<String> cod3){
		ArrayList<String> cod = new ArrayList<String>();
		cod.addAll(cod1);
		cod.addAll(cod2);
		cod.addAll(cod3);
		return cod;
	}
	
	/**
	 * Crea un array list de string con el string que le entra como parametro
	 * @param str
	 * @return
	 */
	public static ArrayList<String> creaCod(String str){
		ArrayList<String> cod = new ArrayList<String>();
		cod.add(str);
		return cod;
	}
	
	/**
	 * Devuelve un array list vacios
	 * @return
	 */
	public static ArrayList<String> creaCod(){
		return new ArrayList<String>();
	}
        
        public static TablaDeSimbolos creaTS(String id, String clase, Tipo tipo, int dir){
            TablaDeSimbolos ts = new TablaDeSimbolos();
            if (!tipo.getTipo().equals("errorTipo"))
                ts.anadeTS(id, clase, tipo, dir);
            return ts;
        }
        
        public static TablaDeSimbolos creaTS(){
            TablaDeSimbolos ts = new TablaDeSimbolos();
           return ts;
        }
      
        
        public static TablaDeSimbolos creaTS(TablaDeSimbolos ts, String id, String clase, ArrayList<Boolean> err, Tipo tipo, int dir){
            TablaDeSimbolos tsNueva = new TablaDeSimbolos(ts);
            if (!hayError(err) && !tipo.getTipo().equals("errorTipo")){
                if (clase.equals("tipo")) tsNueva.anadeTS(id, clase, tipo, 0); 
                else tsNueva.anadeTS(id, clase, tipo, dir);
            }
                
            return tsNueva;
        }
        
        
        /**
         * FIXME
         * @param tipo
         * @param clase 
         */
        public static int tamDe(Tipo tipo, String clase){
            if (clase.equals("tipo")) return 0;
            else {
                // comprobar si es por var o por valor para subprogramas me parece
                return tipo.getTam();
            }
        }
        
        /**
         * @param tipo
         * @param valor
         * @return 
         */
        public static float valorDe(Tipo tipo, String valor){
            try{
            if (tipo.getTipo().equals("boolean")){
                if (valor.equals("true")) return 1;
                else return 0;
            }
            if (tipo.getTipo().equals("character")){
                return valor.charAt(1);
            }
            if (tipo.getTipo().equals("natural"))
                return Integer.parseInt(valor);

            if (tipo.getTipo().equals("integer"))
                 return Integer.parseInt(valor);
            
            if (tipo.getTipo().equals("float"))
                 return Float.parseFloat(valor);
            
            return 0;
            }
            catch(Exception e){return 0;}
        }
      
      /**
         * @param tipo
         * @param valor
         * @return 
         */
        public static Tipo tipoDe(Tipo tipo, String valor){
           try{ 
                if (tipo.getTipo().equals("boolean") && (valor.equals("true") || valor.equals("false")))
                    return tipo;
                else if (tipo.getTipo().equals("natural") && ((TipoNatural)tipo).getValor()>=0 ){
                    int i = Integer.parseInt(valor);
                    
                   return tipo;
                }  else if (tipo.getTipo().equals("integer") ){
                     int i = Integer.parseInt(valor);
                     return tipo;
                } else if (tipo.getTipo().equals("float")) {
                    float i= Float.parseFloat(valor);
                    return tipo;
                } else if (tipo.getTipo().equals("character") && valor.length()==3)
                {
                 return tipo;
                }
                else return new TipoError();
            }
           catch(Exception e)
           { return new TipoError(); }

        }
        
        /**
         * @param id
         * @param ts
         * @return 
         */
        public static Tipo tipoDe(String id, TablaDeSimbolos ts){
            if (ts.existeId(id) && !ts.esConst(id))
                return new TipoIdentificador(id,ts.getTipo(id).getTam());
            else return new Tipo("errorTipo");
        }        
          
        public static int numInstrucciones(ArrayList<String> cod){
            return cod.size();
        }
        
        /**
         * FIXME hacer bieeeeeeeen
         * @param tipo
         * @param valor
         * @return 
         */
        public static ArrayList<String> inicializarTipo(Tipo tipo, int dir, TablaDeSimbolos ts){
            ArrayList<String> al = new ArrayList<String>(); 
            
            if (esTipoBasico(tipo)){
                al.addAll(inicializarTipoBasico(tipo, dir));
            } else if (tipo.getTipo().equals("array")){
                al.addAll(inicializarTipoArray((TipoArray)tipo, dir, ts));
            } else if (tipo.getTipo().equals("tupla")){
                al.addAll(inicializarTipoTupla((TipoTupla)tipo, dir,ts));
            } else if (tipo.getTipo().equals("ref"))
                al.addAll(inicializarTipo(ts.getTipo(((TipoIdentificador)tipo).getId()),dir,ts));
            return al;
        }
        
        private static ArrayList<String> inicializarTipoBasico(Tipo tipo, int dir){
            ArrayList<String> al = new ArrayList<String>(); 
            al.add("apila(0)");
            al.add("desapila_dir(" + dir + ")");
            return al;
        }
        
        private static ArrayList<String> inicializarTipoArray(TipoArray tipo, int dir, TablaDeSimbolos ts){
            ArrayList<String> al = new ArrayList<String>(); 
            for(int i = 0; i < tipo.getNumElem(); i++){
                al.addAll(inicializarTipo(tipo.getTipoBase(), dir, ts));
                dir += ((TipoArray)tipo).getTipoBase().getTam();
            }
            
            return al;
        }
        
        private static ArrayList<String> inicializarTipoTupla(TipoTupla tipo, int dir, TablaDeSimbolos ts){
            ArrayList<String> al = new ArrayList<String>(); 
            ArrayList<ContenidoTupla> tupla = tipo.getCampos();
            Iterator<ContenidoTupla> it = tupla.iterator();
            
            while(it.hasNext()){
                ContenidoTupla campo = it.next();
                al.addAll(inicializarTipo(campo.getTipo(), dir + campo.getDesp(), ts));
            }
            
            return al;
        }
             
        
        public static Tipo creaBoolean(){
            return new TipoBoolean();
        }
        
        public static Tipo creaCharacter(){
            return new TipoCharacter();
        }
        
        public static Tipo creaNatural(){
            return new TipoNatural();
        }
        
        public static Tipo creaInteger(){
            return new TipoInteger();
        }
        
        public static Tipo creaFloat(){
            return new TipoFloat();
        }
        
        public static Tipo creaArray(int tam, Tipo tipo){
            if (tipo.getTipo().equals("errorTipo"))
                return new TipoError();
            return new TipoArray(tam, tipo);
        }

    public static boolean esTipoBasico(Tipo tipo) {
        return tipo.getTipo().equals("boolean") || tipo.getTipo().equals("character") ||
                tipo.getTipo().equals("natural") || tipo.getTipo().equals("integer") ||
                tipo.getTipo().equals("float");
    }

    public static Tipo creaTupla(ArrayList <ContenidoTupla> campos, ArrayList <Boolean> err, Tipo tipo) {
        if (!hayError(err) && !tipo.getTipo().equals("errorTipo"))
            return new TipoTupla(campos);
        else return new TipoError();
    }

    public static Tipo creaTuplaVacia() {
        return new TipoTupla(new ArrayList<ContenidoTupla>());
    }
    
    
    public static Hashtable<String,Tipo> consultaTipoResultante() {
        Hashtable<String,Tipo> tablaOperadores = new Hashtable<String,Tipo>();
        tablaOperadores.put("naturalnaturalmenor",new TipoBoolean());
        tablaOperadores.put("naturalintegermenor",new TipoBoolean());
        tablaOperadores.put("naturalfloatmenor",new TipoBoolean());
        tablaOperadores.put("integernaturalmenor",new TipoBoolean());
        tablaOperadores.put("integerintegermenor",new TipoBoolean());
        tablaOperadores.put("integerfloatmenor",new TipoBoolean());
        tablaOperadores.put("floatnaturalmenor",new TipoBoolean());
        tablaOperadores.put("floatintegermenor",new TipoBoolean());
        tablaOperadores.put("floatfloatmenor",new TipoBoolean());
        tablaOperadores.put("charactercharactermenor",new TipoBoolean());
        tablaOperadores.put("booleanbooleanmenor",new TipoBoolean());

        tablaOperadores.put("naturalnaturalmayor",new TipoBoolean());
        tablaOperadores.put("naturalintegermayor",new TipoBoolean());
        tablaOperadores.put("naturalfloatmayor",new TipoBoolean());
        tablaOperadores.put("integernaturalmayor",new TipoBoolean());
        tablaOperadores.put("integerintegermayor",new TipoBoolean());
        tablaOperadores.put("integerfloatmayor",new TipoBoolean());
        tablaOperadores.put("floatnaturalmayor",new TipoBoolean());
        tablaOperadores.put("floatintegermayor",new TipoBoolean());
        tablaOperadores.put("floatfloatmayor",new TipoBoolean());
        tablaOperadores.put("charactercharactermayor",new TipoBoolean());
        tablaOperadores.put("booleanbooleanmayor",new TipoBoolean());

        tablaOperadores.put("naturalnaturalmenorIgual",new TipoBoolean());
        tablaOperadores.put("naturalintegermenorIgual",new TipoBoolean());
        tablaOperadores.put("naturalfloatmenorIgual",new TipoBoolean());
        tablaOperadores.put("integernaturalmenorIgual",new TipoBoolean());
        tablaOperadores.put("integerintegermenorIgual",new TipoBoolean());
        tablaOperadores.put("integerfloatmenorIgual",new TipoBoolean());
        tablaOperadores.put("floatnaturalmenorIgual",new TipoBoolean());
        tablaOperadores.put("floatintegermenorIgual",new TipoBoolean());
        tablaOperadores.put("floatfloatmenorIgual",new TipoBoolean());
        tablaOperadores.put("charactercharactermenorIgual",new TipoBoolean());
        tablaOperadores.put("booleanbooleanmenorIgual",new TipoBoolean());

        tablaOperadores.put("naturalnaturalmayorIgual",new TipoBoolean());
        tablaOperadores.put("naturalintegermayorIgual",new TipoBoolean());
        tablaOperadores.put("naturalfloatmayorIgual",new TipoBoolean());
        tablaOperadores.put("integernaturalmayorIgual",new TipoBoolean());
        tablaOperadores.put("integerintegermayorIgual",new TipoBoolean());
        tablaOperadores.put("integerfloatmayorIgual",new TipoBoolean());
        tablaOperadores.put("floatnaturalmayorIgual",new TipoBoolean());
        tablaOperadores.put("floatintegermayorIgual",new TipoBoolean());
        tablaOperadores.put("floatfloatmayorIgual",new TipoBoolean());
        tablaOperadores.put("charactercharactermayorIgual",new TipoBoolean());
        tablaOperadores.put("booleanbooleanmayorIgual",new TipoBoolean());

        tablaOperadores.put("naturalnaturaligual",new TipoBoolean());
        tablaOperadores.put("naturalintegerigual",new TipoBoolean());
        tablaOperadores.put("naturalfloatigual",new TipoBoolean());
        tablaOperadores.put("integernaturaligual",new TipoBoolean());
        tablaOperadores.put("integerintegerigual",new TipoBoolean());
        tablaOperadores.put("integerfloatigual",new TipoBoolean());
        tablaOperadores.put("floatnaturaligual",new TipoBoolean());
        tablaOperadores.put("floatintegerigual",new TipoBoolean());
        tablaOperadores.put("floatfloatigual",new TipoBoolean());
        tablaOperadores.put("charactercharacterigual",new TipoBoolean());
        tablaOperadores.put("booleanbooleanigual",new TipoBoolean());

        tablaOperadores.put("naturalnaturaldistinto",new TipoBoolean());
        tablaOperadores.put("naturalintegerdistinto",new TipoBoolean());
        tablaOperadores.put("naturalfloatdistinto",new TipoBoolean());
        tablaOperadores.put("integernaturaldistinto",new TipoBoolean());
        tablaOperadores.put("integerintegerdistinto",new TipoBoolean());
        tablaOperadores.put("integerfloatdistinto",new TipoBoolean());
        tablaOperadores.put("floatnaturaldistinto",new TipoBoolean());
        tablaOperadores.put("floatintegerdistinto",new TipoBoolean());
        tablaOperadores.put("floatfloatdistinto",new TipoBoolean());
        tablaOperadores.put("charactercharacterdistinto",new TipoBoolean());
        tablaOperadores.put("booleanbooleandistinto",new TipoBoolean());

        tablaOperadores.put("naturalnaturalsuma",new TipoNatural());
        tablaOperadores.put("naturalintegersuma",new TipoNatural());
        tablaOperadores.put("naturalfloatsuma",new TipoFloat());
        tablaOperadores.put("integernaturalsuma",new TipoNatural());
        tablaOperadores.put("integerintegersuma",new TipoNatural());
        tablaOperadores.put("integerfloatsuma",new TipoFloat());
        tablaOperadores.put("floatnaturalsuma",new TipoFloat());
        tablaOperadores.put("floatintegersuma",new TipoFloat());
        tablaOperadores.put("floatfloatsuma",new TipoFloat());

        tablaOperadores.put("naturalnaturalresta",new TipoNatural());
        tablaOperadores.put("naturalintegerresta",new TipoNatural());
        tablaOperadores.put("naturalfloatresta",new TipoFloat());
        tablaOperadores.put("integernaturalresta",new TipoNatural());
        tablaOperadores.put("integerintegerresta",new TipoNatural());
        tablaOperadores.put("integerfloatresta",new TipoFloat());
        tablaOperadores.put("floatnaturalresta",new TipoFloat());
        tablaOperadores.put("floatintegerresta",new TipoFloat());
        tablaOperadores.put("floatfloatresta",new TipoFloat());

        tablaOperadores.put("naturalnaturalmultiplica",new TipoNatural());
        tablaOperadores.put("naturalintegermultiplica",new TipoNatural());
        tablaOperadores.put("naturalfloatmultiplica",new TipoFloat());
        tablaOperadores.put("integernaturalmultiplica",new TipoNatural());
        tablaOperadores.put("integerintegermultiplica",new TipoNatural());
        tablaOperadores.put("integerfloatmultiplica",new TipoFloat());
        tablaOperadores.put("floatnaturalmultiplica",new TipoFloat());
        tablaOperadores.put("floatintegermultiplica",new TipoFloat());
        tablaOperadores.put("floatfloatmultiplica",new TipoFloat());

        tablaOperadores.put("naturalnaturaldivide",new TipoNatural());
        tablaOperadores.put("naturalintegerdivide",new TipoNatural());
        tablaOperadores.put("naturalfloatdivide",new TipoFloat());
        tablaOperadores.put("integernaturaldivide",new TipoNatural());
        tablaOperadores.put("integerintegerdivide",new TipoNatural());
        tablaOperadores.put("integerfloatdivide",new TipoFloat());
        tablaOperadores.put("floatnaturaldivide",new TipoFloat());
        tablaOperadores.put("floatintegerdivide",new TipoFloat());
        tablaOperadores.put("floatfloatdivide",new TipoFloat());

        tablaOperadores.put("booleanbooleanand",new TipoBoolean());
        tablaOperadores.put("booleanbooleanor",new TipoBoolean()); 

        tablaOperadores.put("naturalnaturaldesplazarDer",new TipoNatural());
        tablaOperadores.put("naturalnaturaldesplazarIzq",new TipoNatural());

        tablaOperadores.put("naturalnaturalmodulo",new TipoNatural());
        tablaOperadores.put("integernaturalmodulo",new TipoNatural());

        tablaOperadores.put("naturalnegativo",new TipoNatural());
        tablaOperadores.put("integernegativo",new TipoInteger());
        tablaOperadores.put("floatnegativo",new TipoFloat());
        tablaOperadores.put("naturalnegativo",new TipoNatural());

        tablaOperadores.put("booleannot",new TipoBoolean());			

        tablaOperadores.put("natural(float)",new TipoFloat());
        tablaOperadores.put("integer(float)",new TipoFloat());
        tablaOperadores.put("boolean(float)",new TipoFloat());
        tablaOperadores.put("float(float)",new TipoFloat());
        tablaOperadores.put("character(float)",new TipoFloat());

        tablaOperadores.put("natural(nat)",new TipoNatural());
        tablaOperadores.put("character(nat)",new TipoNatural());

        tablaOperadores.put("natural(int)",new TipoNatural());
        tablaOperadores.put("integer(int)",new TipoNatural());
        tablaOperadores.put("float(int)",new TipoNatural());
        tablaOperadores.put("character(int)",new TipoNatural());


        tablaOperadores.put("natural(char)",new TipoCharacter());
        tablaOperadores.put("character(char)",new TipoCharacter());
        
        return tablaOperadores;
    }
    
    public static Tipo consultarTipoResultante (String s){
        Hashtable<String, Tipo> tablaOperadores = consultaTipoResultante();
        Tipo tipo = tablaOperadores.get(s);
        if (tipo != null) return tipo;
        else return new TipoError();
    }
    
    /*
      -> Si t1 es de tipo ref, buscar la expresión t'1 referida en la tabla de
        símbolos y comprobar la compatibilidad entre t'1 y t2
      -> Si no, si t2 es de tipo ref, buscar la expresión t'2 referida en la tabla de
         símbolos y comprobar la compatibilidad entre t1 y t'2
      -> Si no, si las etiquetas de t1 y t2 coindicen
      -> Si el número de hijos de t1 y t2 coinciden t1 y t2 serán compatibles si lo son cada uno
         de sus hijos
      -> Si no, decidir que no son compatibles
    */
    public static boolean tiposCompatibles(Tipo tipo1, Tipo tipo2, TablaDeSimbolos ts) {
        tipo1 = tipoRec(tipo1,ts);
        tipo2 = tipoRec(tipo2,ts);
        if(tipo1.getTipo().equals("character") && tipo2.getTipo().equals("character")) return true;    
        if(tipo1.getTipo().equals("boolean") && tipo2.getTipo().equals("boolean")) return true;    
        if(tipo1.getTipo().equals("natural") && tipo2.getTipo().equals("natural")) return true;
        if(tipo1.getTipo().equals("integer") && tipo2.getTipo().equals("integer")) return true;
        if(tipo1.getTipo().equals("integer") && tipo2.getTipo().equals("natural")) return true;
        if(tipo1.getTipo().equals("float") && tipo2.getTipo().equals("float")) return true;
        if(tipo1.getTipo().equals("float") && tipo2.getTipo().equals("integer")) return true;
        if(tipo1.getTipo().equals("float") && tipo2.getTipo().equals("natural")) return true;
        if(tipo1.getTipo().equals("array")&&tipo2.getTipo().equals("array"))
            return ((TipoArray)tipo1).getNumElem()==((TipoArray)tipo2).getNumElem() && 
                    tiposCompatibles(((TipoArray)tipo1).getTipoBase(),((TipoArray)tipo2).getTipoBase(),ts);
        if(tipo1.getTipo().equals("tupla") && tipo2.getTipo().equals("tupla")){
            if (((TipoTupla)tipo1).getCampos().size() != ((TipoTupla)tipo2).getCampos().size()) return false;
            for (int i=0; i<((TipoTupla)tipo1).getCampos().size(); i++)
                if(!tiposCompatibles(((TipoTupla)tipo1).getTipoTuplaPos(i),((TipoTupla)tipo2).getTipoTuplaPos(i),ts)) return false;
            return true;
        }
        return false;
   }


        /* Si tipo.t = ref entonces
            si existeID(ts,tipo.id) devuelve ref!(ts[tipo.id].tipo,ts)
            si no <t:err>
            si no devuelve tipo */
       public static Tipo tipoRec(Tipo tipo, TablaDeSimbolos tabla) {

           if(tipo.getTipo().equals("ref")){
               String id = ((TipoIdentificador)tipo).getId();
               Tipo tipoRec = tabla.getTipo(id);
               if (!tipoRec.getTipo().equals("errorTipo")) return tipoRec(tipoRec,tabla);
           }
           return tipo;
       }

    public static TablaDeSimbolos eliminarTablaLocal(TablaDeSimbolos ts) {
        TablaDeSimbolos tsNueva = new TablaDeSimbolos(ts);
        tsNueva.eliminaTSLocal();
        return tsNueva;
    }
    
    public static TablaDeSimbolos creaTablaLocal(TablaDeSimbolos ts) {
         TablaDeSimbolos tsNueva = new TablaDeSimbolos(ts);
         tsNueva.creaTSLocal();
         return tsNueva;
    }

    public static Object creaProc(ArrayList<ContenidoProc> lista, String id, Tipo tipo1, Tipo tipo2) {
        if (!tipo1.getTipo().equals("errorTipo") && !tipo2.getTipo().equals("errorTipo"))
            return new TipoProc(lista);
        else return new TipoError();
    }
    
    public static ArrayList<String> generaPrologo(int tamVariables) {
        
        ArrayList<String> prologo = new ArrayList<String>();
        // guarda en CP + 2 el registro B
        prologo.add("apila_dir(0)");
        prologo.add("apila(2)");
        prologo.add("suma");
        prologo.add("apila_dir(1)"); // 1 + nivel ¿? sera para anidados
        prologo.add("desapila_ind");
        // B apunta al comienzo de las variables subprog -> CP + 3
        prologo.add("apila_dir(0)");
        prologo.add("apila(3)");
        prologo.add("suma");
        prologo.add("desapila_dir(1)");
        // CP apunta al final del reg de activacion
        prologo.add("apila_dir(0)");
        prologo.add("apila(" + (tamVariables + 2) + ")");
        prologo.add("suma");
        prologo.add("desapila_dir(0)");
        
        return prologo;
    }
    
    public static ArrayList<String> generaEpilogo(int tamVariables) {
        
        ArrayList<String> epilogo = new ArrayList<String>();
        // restaura CP
        epilogo.add("apila_dir(0)");
        epilogo.add("apila(" + (tamVariables + 2) + ")");
        epilogo.add("resta");
        epilogo.add("desapila_dir(0)");
        // restaura B
        epilogo.add("apila_dir(0)");
        epilogo.add("apila(2)");
        epilogo.add("suma");
        epilogo.add("apila_ind"); 
        epilogo.add("desapila_dir(1)"); 
        // obtiene dir de ret
        epilogo.add("apila_dir(0)");
        epilogo.add("apila(1)");
        epilogo.add("suma");
        epilogo.add("apila_ind");        
        
        return epilogo;
    }
    public static ArrayList<String> cod_dirRetorno( int dir) {
        
        ArrayList<String> ret = new ArrayList<String>();
       
       
        ret.add("apila_dir(0)");
        ret.add("apila(1)");
        ret.add("suma");

        ret.add("apila(" + dir + ")");        
        ret.add("desapila_ind");

        
        return ret;
    }
}
